Practical Topics
Type Assertions vs Type Casting
Type Assertions in TypeScript
- A type assertion tells the TypeScript compiler: “I know more about this value’s type than you do.”
- It doesn’t change the actual runtime value — it’s only a compile-time hint.
There are two ways to write type assertions:
// Angle-bracket syntax
let value: unknown = "hello";
let strLength = (<string>value).length;
// `as` syntax (preferred in modern TS)
let strLength2 = (value as string).length;
Both mean: “Treat value as a string.”
Example
type Person = {
name: string;
age: number;
};
const person = {} as Person;
person.name = "Alice";
person.age = 25;
- The empty object
{}doesn’t matchPerson. - But
as Persontells TypeScript: “trust me, this is aPerson.” - Dangerous if misused:
const person = {} as Person;
console.log(person.age.toFixed()); // Runtime error (age is undefined)
Type assertions don’t do runtime checks. They only silence the compiler.
Type Casting
In JavaScript, type casting usually means converting values from one type to another at runtime, e.g.:
let num = Number("123"); // string → number
let str = String(123); // number → string
let bool = Boolean(1); // number → boolean
This is runtime conversion, not just a compile-time hint.
const str = "123";
const num: number = Number(str); // runtime conversion
"123"is actually converted into123.- If you pass
"abc",Number("abc")→NaN.
Differences between Type Casting and Type Assertion
| Feature | Type Assertion (as, <T>) | Type Casting (runtime conversion) |
|---|---|---|
| When it happens | Compile-time only | Runtime (JS execution) |
| Effect | Just changes how TS checks types | Actually changes the data value |
| Safety | Can be unsafe if misused | Safe but may fail (e.g., Number("abc") → NaN) |
| Example | (value as string).length | Number("123") → 123 |
- Use type assertions sparingly, only when you’re sure about the type.
- Use type casting (conversion functions) when you really need to change the data type.
- Avoid overusing assertions like
as any→ defeats the purpose of TypeScript.
Non-null Assertion Operator !
The non-null assertion operator (!) tells TypeScript:
“I’m sure this value is not null or undefined, trust me.”
It doesn’t do anything at runtime — it’s only a compile-time hint to the TypeScript type checker.
Why Do We Need It?
TypeScript has strict null checks (strictNullChecks: true in tsconfig.json), meaning:
let value: string | null = null;
// Error ❌: value might be null
console.log(value.length);
The compiler complains because value could be null.
But sometimes, you know for sure that it won’t be null at runtime. That’s when you use !.
let value: string | null = "Hello";
// Tell TypeScript: value is definitely not null
console.log(value!.length); // ✅ OK
Here:
- Without
!, TS warns thatvaluemight benull. - With
!, TS trusts you.
Strict Mode (strictNullChecks, etc.)
Strict mode is a set of compiler options that make TypeScript more type-safe and strict.
You enable it in your tsconfig.json:
{
"compilerOptions": {
"strict": true
}
}
strict: true is shorthand for turning on all strict type-checking options.
strictNullChecks
null and undefined are not assignable to other types unless explicitly allowed.
let name: string = "Alice";
name = null; // ❌ Error
name = undefined; // ❌ Error
To allow null/undefined, you must declare them explicitly:
let name: string | null = null; // ✅
This avoids runtime errors like: Cannot read property 'length' of null.
strictPropertyInitialization
Ensures class properties are initialized before use.
class Person {
name: string; // ❌ Error: Property 'name' has no initializer
}
Fix 1: Initialize directly
class Person {
name: string = "Default";
}
Fix 2: Initialize in constructor
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
Fix 3: Use definite assignment assertion (!)
class Person {
name!: string; // tells TS: "I will assign this before use"
}
noImplicitAny
Disallows variables/functions from having an implicit any type.
function greet(message) {
// ❌ Error: message implicitly has type 'any'
console.log(message);
}
Fix: Add explicit type
function greet(message: string) {
console.log(message);
}
This prevents TypeScript from silently defaulting to any.
strictFunctionTypes
Makes function parameter types checked more strictly, ensuring safe assignments.
type Fn = (x: number) => void;
let f1: Fn;
let f2 = (x: number | string) => {};
f1 = f2; // ❌ Error in strict mode
Prevents unsafe function assignments.
strictBindCallApply
Ensures the methods .bind, .call, and .apply are type-safe.
function greet(name: string) {
return `Hello ${name}`;
}
greet.call(null, "Alice"); // ✅
greet.call(null, 123); // ❌ Error
Without this, .call could accept wrong arguments silently.
alwaysStrict
Ensures that every file is parsed in ECMAScript strict mode ("use strict";).
// with alwaysStrict: true
"use strict"; // enforced automatically
This matches modern JavaScript behavior and avoids subtle bugs.